package buffer

import (
	"container/list"
	"math/rand"
)

type Buff struct {
	posi  int
	nega  int
	zero  int
	p1    chan *list.Element
	p2    chan *list.Element
	p3    chan *list.Element
	lis1  []int
	lis2  []int
	lis3  []int
	putt  int
	gett  int
	bf    chan int
	lis   *list.List
	lfput int
	lfget int
	block int
}

func New(sz1, sz2, sz3, putt, gett, sz int) *Buff {
	return &Buff{
		posi:  0,
		nega:  0,
		zero:  0,
		p1:    make(chan *list.Element, sz1),
		p2:    make(chan *list.Element, sz2),
		p3:    make(chan *list.Element, sz3),
		lis1:  make([]int, 0),
		lis2:  make([]int, 0),
		lis3:  make([]int, 0),
		putt:  putt,
		gett:  gett,
		bf:    make(chan int, sz),
		lis:   list.New(),
		lfput: putt,
		lfget: gett,
		block: 0,
	}
}

func NewNumber() int {
	min := -100
	max := 100
	return rand.Intn(max-min) + min
}

func (b *Buff) Do() {
	b.lfput--
	if b.lfput == 0 {
		b.lfput = b.putt
		v := NewNumber()
		go b.PushNumber(v)
	}
	b.lfget--
	if b.lfget == 0 {
		b.lfget = b.gett
		go b.GetNumber()
	}
}

func (b *Buff) Undo(v int) {
	b.lfput--
	if b.lfput == 0 {
		b.lfput = b.putt
		go b.PushNumber(v)
	}
	b.lfget--
	if b.lfget == 0 {
		b.lfget = b.gett
		go b.GetNumber()
	}
}

func (b *Buff) PushNumber(v int) {
	b.block++
	b.bf <- v
	ele := b.lis.PushBack(v)
	if v > 0 {
		b.p1 <- ele
	} else if v < 0 {
		b.p2 <- ele
	} else {
		b.p3 <- ele
	}
	b.block--
}

func (b *Buff) GetNumber() {
	var x *list.Element
	b.block++
	v := <-b.bf
	if v > 0 {
		x = <-b.p1
		b.posi++
		b.lis1 = append(b.lis1, v)
	} else if v < 0 {
		x = <-b.p2
		b.nega++
		b.lis2 = append(b.lis2, v)
	} else {
		x = <-b.p3
		b.zero++
		b.lis3 = append(b.lis3, v)
	}
	b.lis.Remove(x)
	b.block--
}

func (b *Buff) Getp1() []int {
	return b.lis1
}

func (b *Buff) Getp2() []int {
	return b.lis2
}

func (b *Buff) Getp3() []int {
	return b.lis3
}

func (b *Buff) Getlis() *list.List {
	return b.lis
}

func (b *Buff) Getlfget() int {
	return b.lfget
}

func (b *Buff) Getlfput() int {
	return b.lfput
}

func (b *Buff) Getblock() int {
	return b.block
}
